#include <i264e_common.h>
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
/*
 * m:相邻两个基本帧间增强帧的总个数
 * n:一个IDR帧或基本帧被其他基本帧参考的总个数
 */
static int i264e_init_skip_param(i264e_skip_header_t *h, int m, int n, int max_skiptype, int *rdnum, int *rdsnum)
{
	i264e_skip_param_t *sp = &h->skip_info.skip_param;
	int max_rdnum = 0;

	if (h->b_buf_share) {		//only for n1x
		sp->nsize = 1;
		sp->msize = 0;
		sp->rdnum = h->b_mref ? 1 : 0;
		sp->rdsnum = 1;
		*rdnum = sp->rdnum;
		*rdsnum = sp->rdsnum;
	} else {
		switch (h->skip_info.skip_type) {
			case C_STYPE_N1X:
				sp->nsize = 1;
				sp->msize = 0;
				sp->rdnum = h->b_mref ? 3 : 2;
				sp->rdsnum = 0;
				break;
			case C_STYPE_N2X:
				sp->nsize = 1;
				sp->msize = 1;
				sp->rdnum = 2;
				sp->rdsnum = 0;
				break;
			case C_STYPE_N4X:
				sp->nsize = 2;
				sp->msize = 1;
				sp->rdnum = 3;
				sp->rdsnum = 0;
				break;
			case C_STYPE_HN1_FALSE:
				sp->nsize = (h->gopsize - 1) / 2;
				sp->msize = 1;
				sp->rdnum = 3;
				sp->rdsnum = 0;
				break;
			case C_STYPE_HN1_TRUE:
				sp->nsize = c_clip3(n, 1, (h->gopsize - 1) / 2);
				sp->msize = 1;
				sp->rdnum = 3;
				sp->rdsnum = 0;
				break;
			case C_STYPE_H1M_FALSE:
				sp->nsize = 1;
				sp->msize = h->mscenecnt ? h->gopsize - 1 : c_clip3(m, 0, h->gopsize - 1);
				sp->rdnum = 3;
				sp->rdsnum = 0;
				break;
			case C_STYPE_H1M_TRUE:
				sp->nsize = 1;
				sp->msize = h->mscenecnt ? h->gopsize - 1 : c_clip3(m, 0, h->gopsize - 1);
				sp->rdnum = 3;
				sp->rdsnum = 0;
				break;
			default:
				return -1;
		}

		max_rdnum =  (max_skiptype == C_STYPE_N1X && h->b_mref) ? 3 : (max_skiptype < C_STYPE_N4X ? 2 : 3);
		sp->rdnum = sp->rdnum > max_rdnum ? sp->rdnum : max_rdnum;
		sp->rdsnum = 0;
		*rdnum = sp->rdnum;
		*rdsnum = sp->rdsnum;
	}

	return 0;
}

int i264e_init_skip_header(i264e_skip_header_t *h, int gopsize, int i_skip_type, int m, int n, int mscenecnt,
		int max_skiptype, int *rdnum, int *rdsnum, int b_mref, int b_buf_share)
{
	memset(h, 0, sizeof(i264e_skip_header_t));

	h->gopsize = gopsize;
	h->mscenecnt = mscenecnt;
	h->b_mref = b_mref;
	h->b_buf_share = b_buf_share;
	h->skip_info.skip_type = i_skip_type;

	return i264e_init_skip_param(h, m, n, max_skiptype, rdnum, rdsnum);
}

static int select_in2(int x)
{
	int i = 0;

	for (i = 0; i < 2; i++) {
		if (x == i) {
			continue;
		}
		return i;
	}

	return -1;
}

static int select_in3(int x, int y)
{
	int i = 0;

	for (i = 0; i < 3; i++) {
		if ((x == i) || (y == i)) {
			continue;
		}
		return i;
	}

	return -1;
}

static int i264e_reset_rd(i264e_skip_header_t *h, int i_frame)
{
	i264e_rdposidx_t *rdpb = &h->rdposidx_pbase;
	i264e_rdposidx_t *rdpc = &h->rdposidx_cur;

	if (h->b_buf_share) {	/* only n1x support */
		if (i_frame == 0) {
			memset(rdpc, 0, sizeof(i264e_rdposidx_t));
			memset(rdpb, 0, sizeof(i264e_rdposidx_t));
			rdpc->i_frame = i_frame;
			rdpc->x = -1;
			rdpc->y = 0;
			rdpb->i_frame = i_frame;
			rdpb->x = -1;
			rdpb->y = -1;
			rdpc->fsktype = C_FS_IDR;
			/*
			 * if h->b_mref == 1, should use rdframe array to get long referrece,
			 * else should use rdsframe array to get short referrece.
			 * but both to all, for the fist frame, the usage of index is same.
			 */
			rdpc->b_long = h->b_mref ? 1 : 0;
		} else {
			rdpc->i_frame = i_frame;
			/*
			 * if i_frame != 0, so for the P frame, only use the rdsframe to get referrece and decoder
			 * but when i_frame == 1, the referrece and decoder is not shared, to others shared, so here should
			 * have difference;
			 */
			rdpc->x = 0;	/* i_frame == 1, to rdframe[0], else to rdsframe[0] */
			rdpc->y = 0;	/* to rdsframe[0] */
			rdpc->fsktype = C_FS_SBASE;
			rdpc->b_long = 0;
		}
	} else {
		/*
		 * long referrece base frame refer the long base referrece frame,
		 * and it's deblock frame only it's prev frame deblock frame,so not set
		 */
		if (i_frame == 0) {
			memset(rdpc, 0, sizeof(i264e_rdposidx_t));
			memset(rdpb, 0, sizeof(i264e_rdposidx_t));
			rdpc->i_frame = i_frame;
			rdpc->x = -1;
			rdpc->y = 0;
			rdpb->i_frame = i_frame;
			rdpb->x = -1;
			rdpb->y = -1;
		} else {
			rdpc->i_frame = i_frame;
			if (h->b_mref && (h->skip_info.skip_type == C_STYPE_N1X)) {
				rdpc->x = rdpc->y;
			} else {
				rdpc->x = rdpb->b_long ? rdpb->y : rdpc->y;
			}
			if (h->skip_info.skip_param.rdnum == 2) {
				rdpc->y = select_in2(rdpb->b_long ? rdpb->y : rdpc->y);
			} else {
				rdpc->y = select_in3(rdpb->y, rdpc->y);
			}
		}

		if (h->skip_info.skip_type == C_STYPE_N1X) {
			rdpc->fsktype = (i_frame == 0) ? C_FS_IDR : C_FS_SBASE;
			rdpc->b_long = ((i_frame == 0) && h->b_mref) ? 1 : 0;
		} else if (h->skip_info.skip_type == C_STYPE_H1M_FALSE) {
			rdpc->fsktype = (i_frame == 0) ? C_FS_IDR : C_FS_SBASE;
			rdpc->b_long = (rdpc->fsktype == C_FS_IDR) ? 1 : 0;
		} else {
			rdpc->fsktype = (i_frame == 0) ? C_FS_IDR : C_FS_LBASE;
			rdpc->b_long = 1;
		}
	}

	/* update rdpb */
	if (rdpc->b_long) {
		memcpy(rdpb, rdpc, sizeof(i264e_rdposidx_t));
	}
	return 0;
}

static void i264e_select_brd(i264e_skip_header_t *h, int i_frame)
{
	i264e_rdposidx_t *rdpb = &h->rdposidx_pbase;
	i264e_rdposidx_t *rdpc = &h->rdposidx_cur;

	rdpc->i_frame = i_frame;
	if ((rdpc->fsktype == C_FS_IDR) || (rdpc->fsktype == C_FS_LBASE) || (rdpc->fsktype == C_FS_SBASE)) {
		rdpc->x = rdpc->y;
	} else {
		rdpc->x = rdpb->y;
	}
    if (h->skip_info.skip_param.rdnum == 2) {
        rdpc->y = select_in2(rdpb->b_long ? rdpb->y : rdpc->y);
    } else {
        rdpc->y = select_in3(rdpb->y, rdpc->y);
    }
	rdpc->fsktype = C_FS_SBASE;
	rdpc->b_long = 0;
}

static void i264e_select_erd(i264e_skip_header_t *h, int i_frame)
{
	i264e_rdposidx_t *rdpb = &h->rdposidx_pbase;
	i264e_rdposidx_t *rdpc = &h->rdposidx_cur;

	rdpc->i_frame = i_frame;
	rdpc->x = rdpc->y;
    if (h->skip_info.skip_param.rdnum == 2) {
        rdpc->y = select_in2(rdpb->b_long ? rdpb->y : rdpc->y);
    } else {
        rdpc->y = select_in3(rdpb->y, rdpc->y);
    }
	rdpc->fsktype = C_FS_ENHANCE;
	rdpc->b_long = 0;
}

static int i264e_cal_xy(i264e_skip_header_t *h, int i_frame)
{
	int msize = h->skip_info.skip_param.msize;
	int nsize = h->skip_info.skip_param.nsize;

	if ((i_frame == 0) || ((i_frame % ((msize + 1) * nsize)) == 0)) {
		i264e_reset_rd(h, i_frame);
	} else if (i_frame % (msize + 1) == 0) {
		i264e_select_brd(h, i_frame);
	} else {
		i264e_select_erd(h, i_frame);
	}
	return 0;
}

int i264e_decide_slice_type_and_rd(i264e_t *h, i264e_thread_t *cur)
{
	int ret = 0;

	i264e_rdposidx_t *rdpc = &h->common.skip_h.rdposidx_cur;
	i264e_rdposidx_t *rdpb = &h->common.skip_h.rdposidx_pbase;
	i264e_slice_header_t *sh = &cur->p.sh;

	if ((cur->ip.i_frame < 0) || cur->p.fenc->b_force_idr) {
		cur->ip.i_frame = 0;
	} else if (((h->common.skip_h.skip_info.skip_type == C_STYPE_N1X)
            || (h->common.skip_h.skip_info.skip_type == C_STYPE_H1M_FALSE)
			|| (h->common.skip_h.skip_info.skip_type == C_STYPE_H1M_TRUE))
			&& (h->common.skip_h.mscenecnt > 0)) {
		if ((h->common.param.soc == C_T20 || h->common.param.soc == C_T30 || h->common.param.soc == C_T21 || h->common.param.soc == C_T23) && (((h->cur->ip.i_frame + 1) % h->common.param.i_gop) == 0)
				&& (h->common.param.rc.i_rc_method !=  I264E_RC_CQP)
				&& (h->common.eprc_t21.curFrmSce == SCE_SHARPMOVE)
                && (cur->ip.i_frame >= cur->h->common.param.i_gop)) {
			cur->ip.i_frame = 0;
		} else  {
			cur->ip.i_frame = (cur->ip.i_frame + 1) % (h->common.param.i_gop * h->common.skip_h.mscenecnt);
		}
	} else {
		cur->ip.i_frame = (cur->ip.i_frame + 1) % h->common.param.i_gop;
	}

	if (cur->ip.i_frame == 0) {
		cur->p.i_type = I264E_TYPE_IDR;
	} else {
		cur->p.i_type = I264E_TYPE_P;
	}

	ret = i264e_cal_xy(&h->common.skip_h, cur->ip.i_frame);
	if (rdpc->x == -1) {
		cur->p.fref = NULL;
		if (h->common.param.b_buf_share) {
			if (h->common.param.b_mref) {
				cur->p.fdec = &h->common.rdframe[rdpc->y];		//need rdframe to be long referrence frame
			} else {
				cur->p.fdec = &h->common.rdsframe[rdpc->y];		//need rdframe to be short referrence frame
			}
		} else {
			cur->p.fdec = &h->common.rdframe[rdpc->y];
		}
	} else {
		if (h->common.param.b_buf_share) {
			if (h->common.param.b_mref && (cur->ip.i_frame == 1)) {
				cur->p.fref = &h->common.rdframe[rdpc->x];			// fref should be the I frame decoder frame
				cur->p.fdec = &h->common.rdsframe[rdpc->y];			// fdec should be frame rdsframe to be future shared
			} else {
				i264e_check(rdpc->x == rdpc->y, "use bufshare, frame(%d)'s referrence(%d) must be equal to decoder frame(%d)\n", cur->ip.i_frame, rdpc->x, rdpc->y);
				cur->p.fref = &h->common.rdsframe[rdpc->x];			// now rdpc->x must equal to rdpc->y
				cur->p.fdec = &h->common.rdsframe[rdpc->y];
			}
		} else {
			i264e_check(rdpc->x != rdpc->y, "no bufshare, frame(%d)'s referrence(%d) must't be equal to decoder frame(%d)\n", cur->ip.i_frame, rdpc->x, rdpc->y);
			cur->p.fref = &h->common.rdframe[rdpc->x];
			cur->p.fdec = &h->common.rdframe[rdpc->y];
		}
		if (h->common.param.b_mref) {
			cur->p.fmref = &h->common.rdframe[rdpb->y];		// for mref is a long referrence frame
		}
	}

	cur->p.pic_out.fsktype = rdpc->fsktype;

	sh->b_ref_pic_list_reordering[0] = 0;
	sh->b_ref_pic_list_reordering[1] = 0;
	sh->i_mmco_command_count = 0;
    sh->b_num_ref_idx_override = 0;
    sh->i_num_ref_idx_l0_active = h->common.pps->i_num_ref_idx_l0_default_active;
    sh->i_num_ref_idx_l1_active = 1;

	if ((rdpc->fsktype == C_FS_IDR) && (rdpc->b_long)) {
		sh->i_mmco_command_count = 1;
	} else if ((h->common.skip_h.skip_info.skip_type != C_STYPE_N1X)
			&& ((rdpc->fsktype == C_FS_LBASE) || (rdpc->fsktype == C_FS_SBASE))) {
		sh->b_ref_pic_list_reordering[0] = 1;
		sh->ref_pic_list_order[0][0].idc = 2;
		sh->ref_pic_list_order[0][0].arg = 0;
		if (rdpc->b_long) {
			sh->i_mmco_command_count = 2;
			sh->mmco[0].idc = 1;
			sh->mmco[0].arg = 0;
			sh->mmco[1].idc = 6;
			sh->mmco[1].arg = 0;
		}
	} else if ((h->common.skip_h.skip_info.skip_type == C_STYPE_N1X) && h->common.param.b_mref) {
		if (cur->ip.i_frame == 1) {
			sh->b_num_ref_idx_override = 1;
			sh->i_num_ref_idx_l0_active = 1;
		}
	}

	return ret;
}

void i264e_decide_slice_qp(i264e_t *h, i264e_thread_t *cur)
{
#if 0
	i264e_rdposidx_t *rdpc = &h->common.skip_h.rdposidx_cur;
	i264e_skip_info_t *skip_info = &h->common.skip_h.skip_info;

	if (((rdpc->fsktype == C_FS_LBASE) && !(h->common.skip_h.mscenecnt
			&& ((skip_info->skip_type == C_STYPE_H1M_FALSE) || (skip_info->skip_type == C_STYPE_H1M_TRUE))))
			|| ((h->common.skip_h.mscenecnt == 0) && (rdpc->fsktype == C_FS_SBASE)
				&& (skip_info->skip_type == C_STYPE_H1M_FALSE))) {
		cur->p.i_global_qp = cur->h->common.lastidr_qp;
	}
#endif
}


i264e_frame_t *i264e_get_prev_fram_dec(i264e_t *h, i264e_thread_t *cur)
{
	i264e_rdposidx_t *rdpc = &h->common.skip_h.rdposidx_cur;
	i264e_skip_info_t *skip_info = &h->common.skip_h.skip_info;

    if (((skip_info->skip_type == C_STYPE_H1M_FALSE) && (rdpc->fsktype == C_FS_SBASE))
        || ((skip_info->skip_type == C_STYPE_H1M_TRUE) && (rdpc->fsktype == C_FS_LBASE))) {
        return &h->common.rdframe[select_in3(rdpc->x, rdpc->y)];
    }
    return NULL;
}

void i264e_dec_ref_pic_remark(i264e_t *h, i264e_thread_t *cur)
{
	i264e_rdposidx_t *rdpc = &h->common.skip_h.rdposidx_cur;
	i264e_skip_info_t *skip_info = &h->common.skip_h.skip_info;
	i264e_slice_header_t *sh = &cur->p.sh;

    if (((skip_info->skip_type == C_STYPE_H1M_FALSE) && (rdpc->fsktype == C_FS_SBASE))
        || ((skip_info->skip_type == C_STYPE_H1M_TRUE) && (rdpc->fsktype == C_FS_LBASE))) {
        sh->b_ref_pic_list_reordering[0] = 0;
        sh->b_ref_pic_list_reordering[1] = 0;
        sh->i_mmco_command_count = 0;

        if ((skip_info->skip_type == C_STYPE_H1M_TRUE) && (rdpc->fsktype == C_FS_LBASE)) {
            sh->i_mmco_command_count = 2;
            sh->mmco[0].idc = 1;
            sh->mmco[0].arg = 0;
            sh->mmco[1].idc = 6;
            sh->mmco[1].arg = 0;
        }
        /*
         * here should be complete, for if it's C_FS_LBASE, I can't enforce it to C_FS_ENHANCE, for I had update rdposidx_pbase
         * but by now only smart type using the remark func, so no problem now
         * */
        if ((skip_info->skip_type == C_STYPE_H1M_FALSE) && (rdpc->fsktype == C_FS_SBASE)) {
            cur->p.pic_out.fsktype = C_FS_ENHANCE;
        }
    }
}

int i264e_decide_reset_slice_to_idr(i264e_t *h, i264e_thread_t *cur)
{
	i264e_skip_info_t *skip_info = &h->common.skip_h.skip_info;
	i264e_rdposidx_t *rdpc = &h->common.skip_h.rdposidx_cur;

	if (h->common.param.hskip.b_enable_scenecut
			&& ((skip_info->skip_type == C_STYPE_H1M_FALSE) || (skip_info->skip_type == C_STYPE_H1M_TRUE))
			&& (((cur->ip.i_frame * h->common.param.i_fps_den) % h->common.param.i_fps_num) == 0) && ((rdpc->fsktype == C_FS_LBASE) || (rdpc->fsktype == C_FS_SBASE))
			&& (i264e_ratecontrol_is_larger_lastidr(cur))) {
		int overhead = NALU_OVERHEAD, i = 0;
		int tmp_global_qp = h->cur->p.i_global_qp;

		i264e_ratecontrol_end(cur);
		cur->p.fenc->b_force_idr = 1;
		i264e_ratecontrol_init(h, tmp_global_qp);
		i264e_decide_slice_type_and_rd(h, h->cur);	//be surely to be the IDR type;
		i264e_ratecontrol_start(h->cur);
		i264e_decide_slice_qp(h, h->cur);
		if (h->common.param.i_threads > 1) {
			i264e_threadpool_wait_all(h);
		}
		if (i264e_idr_reconfig(h) < 0) {
			i264e_log(h, C_LOG_WARNING, "i264e_idr_reconfig failed\n");
		}
		if (i264e_reconfig(h) < 0) {
			i264e_log(h, C_LOG_WARNING, "i264e_reconfig failed\n");
		}
		cur->p.pic_in.i_qpplus1 = cur->p.i_global_qp;
		cur->p.fdec->i_poc = cur->p.fenc->i_poc = 2 * cur->ip.i_frame;

		cur->p.i_nal_type = I264E_NAL_SLICE_IDR;
		cur->p.i_nal_ref_idc = I264E_NAL_PRIORITY_HIGHEST;
		cur->p.sh.i_type = I264E_SLICE_TYPE_I;

		cur->p.fdec->i_pts = cur->p.fenc->i_pts;
		cur->p.fdec->i_dts = cur->p.fenc->i_pts;
		cur->p.fdec->timestamp = cur->p.fenc->timestamp;
		bs_init(&h->cur->p.out.bs, h->cur->p.out.p_bitstream, h->cur->p.out.i_bitstream);
		h->cur->p.out.i_nal = 0;

		if(h->cur->p.i_type == I264E_TYPE_IDR) {
			/* Write SPS and PPS */
			if(h->common.param.b_repeat_headers) {
				/* generate sequence parameters */
				i264e_nal_start(h->cur, I264E_NAL_SPS, I264E_NAL_PRIORITY_HIGHEST);
				i264e_sps_write(&h->cur->p.out.bs, h->common.sps);
				if (i264e_nal_end(h->cur)) {
					i264e_log(h, C_LOG_ERROR, "nal sps end failed\n");
					goto err_nal_sps_end;
				}
				overhead += h->cur->p.out.nal[h->cur->p.out.i_nal-1].i_payload
					+ h->cur->p.out.nal[h->cur->p.out.i_nal-1].i_padding
					+ NALU_OVERHEAD;

				/* generate picture parameters */
				i264e_nal_start(h->cur, I264E_NAL_PPS, I264E_NAL_PRIORITY_HIGHEST);
				i264e_pps_write(&h->cur->p.out.bs, h->common.sps, h->common.pps);
				if(i264e_nal_end(h->cur)) {
					i264e_log(h, C_LOG_ERROR, "nal pps end failed\n");
					goto err_nal_pps_end;
				}
				overhead += h->cur->p.out.nal[h->cur->p.out.i_nal-1].i_payload
					+ h->cur->p.out.nal[h->cur->p.out.i_nal-1].i_padding
					+ NALU_OVERHEAD;

#ifdef SIGN_I264E_VERSION
				/* identify ourself */
				/* Avid's decoder strictly wants two SEIs for AVC-Intra so we can't insert the i264e SEI */
				i264e_nal_start(h->cur, I264E_NAL_SEI, I264E_NAL_PRIORITY_DISPOSABLE);
				if( i264e_sei_version_write(h->cur, &h->cur->p.out.bs)) {
					i264e_log(h, C_LOG_ERROR, "sei_version_write failed\n");
					goto err_sei_version_write;
				}
				if(i264e_nal_end(h->cur)) {
					i264e_log(h, C_LOG_ERROR, "sei_version_write end failed\n");
					goto err_sei_version_write_end;
				}
				overhead += h->cur->p.out.nal[h->cur->p.out.i_nal-1].i_payload
					+ SEI_OVERHEAD;
#endif

			}
		}

		/* write extra sei */
		for (i = 0; i < h->cur->p.fenc->extra_sei.num_payloads; i++) {
			i264e_nal_start(h->cur, I264E_NAL_SEI, I264E_NAL_PRIORITY_DISPOSABLE);
			i264e_sei_write(&h->cur->p.out.bs, h->cur->p.fenc->extra_sei.payloads[i].payload, h->cur->p.fenc->extra_sei.payloads[i].payload_size,
					h->cur->p.fenc->extra_sei.payloads[i].payload_type);
			if (i264e_nal_end(h->cur) < 0) {
				i264e_log(h, C_LOG_ERROR, "sei_nal_end to extra_sei %d failed\n", i);
				goto err_extra_sei_nal_end;
			}
			overhead += h->cur->p.out.nal[h->cur->p.out.i_nal-1].i_payload + SEI_OVERHEAD;
			if (h->cur->p.fenc->extra_sei.sei_free) {
				h->cur->p.fenc->extra_sei.sei_free(h->cur->p.fenc->extra_sei.payloads[i].payload);
				h->cur->p.fenc->extra_sei.payloads[i].payload = NULL;
			}
		}

		if (h->cur->p.fenc->extra_sei.sei_free) {
			h->cur->p.fenc->extra_sei.sei_free(h->cur->p.fenc->extra_sei.payloads);
			h->cur->p.fenc->extra_sei.payloads = NULL;
			h->cur->p.fenc->extra_sei.sei_free = NULL;
		}

		/* generate sei pic timing */
		if(h->common.sps->vui.b_pic_struct_present || h->common.sps->vui.b_nal_hrd_parameters_present) {
			i264e_nal_start(h->cur, I264E_NAL_SEI, I264E_NAL_PRIORITY_DISPOSABLE);
			i264e_sei_pic_timing_write(h->cur, &h->cur->p.out.bs);
			if(i264e_nal_end(h->cur)) {
				i264e_log(h, C_LOG_ERROR, "sei_pic_timing_write end failed\n");
				goto err_sei_pic_timing_write_end;
			}
			overhead += h->cur->p.out.nal[h->cur->p.out.i_nal-1].i_payload
				+ SEI_OVERHEAD;
		}
		i264e_slice_init(cur);

		return 1;
	}

	return 0;

err_sei_pic_timing_write_end:
err_extra_sei_nal_end:
#ifdef SIGN_I264E_VERSION
err_sei_version_write_end:
err_sei_version_write:
#endif
err_nal_pps_end:
err_nal_sps_end:
	return -1;
}
